package com.piece.core.web.aspect;

import com.piece.core.framework.annotation.source.CacheSource;
import com.piece.core.framework.constant.FrameWorkConstants;
import com.piece.core.framework.util.string.JsonUtil;
import com.piece.core.framework.util.math.RandomUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.lock.DistributedLock;
import com.piece.core.framework.util.lock.ZLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 分布式缓存切面
 */
@Slf4j
@Aspect
@Order(10)
public class CacheAspect {

    @Autowired(required = false)
    private DistributedLock locker;

    @Autowired
    private com.piece.core.framework.util.cache.Cache cache;

    @Around("@annotation(cacheSource)")
    public Object aroundLock(ProceedingJoinPoint point, CacheSource cacheSource) throws Throwable {
        // 获取注解属性
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        // 获取缓存key的前缀
        String prefix = cacheSource.prefix();
        // 获取目标方法的参数列表
        Object[] args = point.getArgs();
        // 获取目标方法的返回值类型
        Class<?> returnType = method.getReturnType();

        if (!returnType.equals(void.class)) {
            // 从缓存中查询
            String key = prefix + FrameWorkConstants.SEPARATOR_KEY_VALUE +
                    signature.toShortString() + FrameWorkConstants.SEPARATOR_KEY_VALUE +
                    method.getName();
            String json = cache.get(key);
            // 命中返回
            if (StringUtil.isNotEmpty(json)) {
                return JsonUtil.toBean(json, returnType);
            }

            ZLock lockObj = null;
            try {
                // 没有命中,加分布式锁
                lockObj = lock(key);
                // 再次查询缓存,命中返回
                json = cache.get(key);
                // 命中返回
                if (StringUtil.isNotEmpty(json)) {
                    unlock(lockObj);
                    return JsonUtil.toBean(json, returnType);
                }

                // 执行目标方法
                Object result = point.proceed(args);
                // 放入缓存,释放分布式锁
                int timeout = cacheSource.timeout();
                int random = cacheSource.random();
                if (timeout != 0) {
                    cache.setExpire(key, JsonUtil.toJson(result), timeout + RandomUtil.randomInt(random), TimeUnit.SECONDS);
                } else {
                    cache.set(key, JsonUtil.toJson(result));
                }

                return result;
            } finally {
                unlock(lockObj);
            }
        }

        return point.proceed(args);
    }

    private ZLock lock(String key) {
        if (null != locker) {
            return locker.lock(key);
        }

        return null;
    }

    private void unlock(ZLock lock) {
        if (null != locker && null != lock) {
            locker.unlock(lock);
        }
    }
}
